// // Copyright (c) 2010 All Right Reserved // // vl // // 2010-05-01 // Contains ... using System; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Text; namespace LargoCommon.Midi { /// /// Midi File. /// public class MidiFile { #region Constructors /// /// Initializes a new instance of the class. /// /// The file path. public MidiFile(string filePath) { Contract.Requires(this.FilePath != null); //// Validate input if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException(nameof(this.FilePath), "No path provided."); } this.FilePath = filePath; this.Name = Path.GetFileNameWithoutExtension(this.FilePath.Trim()); this.Import(); } /// /// Initializes a new instance of the class. /// /// The file path. /// The given sequence. /// FilePath - No path provided. public MidiFile(string filePath, CompactMidiStrip givenSequence) { Contract.Requires(this.FilePath != null); //// Validate input if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException(nameof(this.FilePath), "No path provided."); } this.FilePath = filePath; this.Name = Path.GetFileNameWithoutExtension(this.FilePath.Trim()); this.Sequence = givenSequence; } #endregion #region Properties /// /// Gets or sets the name. /// /// /// The name. /// public string Name { get; set; } /// /// Gets or sets the file path. /// /// /// The file path. /// public string FilePath { get; set; } /// /// Gets or sets the sequence. /// /// /// The sequence. /// public CompactMidiStrip Sequence { get; set; } /// /// Gets a value indicating whether this instance has valid sequence. /// /// /// True if this instance has valid sequence; otherwise, false. /// public bool HasValidSequence => this.Sequence != null && this.Sequence.Count > 0; #endregion #region String representation /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); s.AppendFormat("Path: {0}", this.FilePath); return s.ToString(); } #endregion /// /// Creates a MIDI file at the specified path and writes the sequence to it. /// public void Save() { Contract.Requires(this.Sequence != null); // Create the output file and save to it using (var stream = new FileStream(this.FilePath, FileMode.Create)) { this.Save(stream); } } #region Private methods - Importing from MIDI file /// /// Reads a MIDI stream into a new CompactMidiStrip. /// private void Import() { //// Open the file //// try { //// using (FileStream inputStream = new FileStream(filePath, FileMode.Open)) { using (var inputStream = new FileStream(this.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { // Parse it and return the sequence this.Import(inputStream); //// Avoid multiple or conditional return statements. } //// } //// IOException //// UnauthorizedAccessException } /// /// Reads a MIDI stream into a new CompactMidiStrip. /// /// The stream containing the MIDI data. /// Input Stream Exception. /// Can't read the MIDI file. - 0 /// or /// Invalid MIDI header. - 0 private void Import(Stream inputStream) { //// Validate input if (inputStream == null) { throw new ArgumentNullException(nameof(inputStream)); } if (!inputStream.CanRead) { throw new MidiParserException("Can't read the MIDI file.", 0); } //// Read in the main MIDI header var mainHeader = MidiFileChunkHeader.Read(inputStream); if (mainHeader.NumberOfLines < 0) { throw new MidiParserException("Invalid MIDI header.", 0); } //// Read in all of the tracks var trackChunks = new MidiTrackChunkHeader[mainHeader.NumberOfLines]; for (var i = 0; i < mainHeader.NumberOfLines; i++) { trackChunks[i] = MidiTrackChunkHeader.Read(inputStream); } // Create the MIDI sequence this.Sequence = new CompactMidiStrip(mainHeader.Format, mainHeader.Division) { Header = { Name = this.Name, //// FilePath = this.FilePath, FileName = Path.GetFileNameWithoutExtension(this.FilePath) } }; for (var i = 0; i < mainHeader.NumberOfLines; i++) { var data = trackChunks[i].GetData(); if (data == null) { continue; } var parser = new MidiParser(data, null); this.Sequence.AddTrack(parser.ParseToTrack()); } this.Sequence.SetTrackNamesFromMeta(); //// 2015/01 if (this.Sequence.Count > 0) { this.Sequence.Header.Metric = this.Sequence[0].Metric; } //// sequence.SetMetricFromTracks(); //// sequence.SetTracksMetric(); this.Sequence.SetTrackInstrumentsFromFirstOccurrence(); } #endregion #region Private mothods - Exporting to MIDI file /// /// Writes a MIDI file header out to the stream. /// /// The output stream. /// This functionality is automatically performed during a Save. private void WriteHeader(Stream outputStream) { // Check parameters if (this.Sequence == null) { throw new ArgumentNullException(nameof(this.Sequence)); } if (outputStream == null) { throw new ArgumentNullException(nameof(outputStream)); } if (!outputStream.CanWrite) { throw new MidiParserException("Can't write to stream.", 0); } if (this.Sequence.Count < 1) { throw new ArgumentOutOfRangeException(nameof(this.Sequence), this.Sequence.Count, "Sequences require at least 1 track."); } // Write out the main header for the sequence var mainHeader = new MidiFileChunkHeader(this.Sequence.Format, this.Sequence.Count, this.Sequence.Header.Division); mainHeader.Write(outputStream); } /// /// Writes the MIDI sequence to the output stream. /// /// The stream to which the MIDI sequence should be written. private void Save(Stream outputStream) { //// Contract.Requires(sequence != null); //// Contract.Requires(outputStream != null); //// Check valid state (as best we can check) if (this.Sequence == null || this.Sequence.Count < 1) { throw new InvalidOperationException("Empty sequence (no musical tracks have been added)."); } //// Check parameters if (outputStream == null) { throw new ArgumentNullException(nameof(outputStream)); } if (!outputStream.CanWrite) { throw new ArgumentException("Can't write to stream.", nameof(outputStream)); } // Write out the main header for the sequence this.WriteHeader(outputStream); // Write out each track in the order it was added to the sequence foreach (var track in this.Sequence.Where(track => track != null)) { track.Write(outputStream); } //// DoNotCatchGeneralExceptionTypes try { catch (Exception exc) .. "Unable to save the sequence to a local file." } #endregion } }